home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / WINGs / wscroller.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-09  |  20.8 KB  |  870 lines

  1.  
  2.  
  3.  
  4.  
  5. #include "WINGsP.h"
  6.  
  7. /* undefine will disable the autoadjusting of the knob dimple to be
  8.  * directly below the cursor 
  9.  * DOES NOT WORK */
  10. #undef STRICT_NEXT_BEHAVIOUR
  11.  
  12. #define AUTOSCROLL_INITIAL_DELAY     200
  13.  
  14. #define AUTOSCROLL_DELAY         40
  15.  
  16.  
  17. typedef struct W_Scroller {
  18.     W_Class widgetClass;
  19.     W_View *view;
  20.  
  21.     void *clientData;
  22.     WMAction *action;
  23.  
  24.     float knobProportion;
  25.     float floatValue;
  26.  
  27.     WMHandlerID timerID;           /* for continuous scrolling mode */
  28.  
  29. #ifndef STRICT_NEXT_BEHAVIOUR
  30.     int dragPoint;               /* point where the knob is being 
  31.                     * dragged */
  32. #endif
  33.     struct {
  34.     WMScrollArrowPosition arrowsPosition:4;
  35.  
  36.     unsigned int horizontal:1;
  37.  
  38.     WMScrollerPart hitPart:4;
  39.  
  40.     /* */    
  41.     unsigned int documentFullyVisible:1;   /* document is fully visible */
  42.     
  43.     unsigned int prevSelected:1;
  44.     
  45.     unsigned int pushed:1;
  46.     
  47.     unsigned int incrDown:1;      /* whether increment button is down */
  48.  
  49.     unsigned int decrDown:1;
  50.     
  51.     unsigned int draggingKnob:1;
  52.  
  53.     unsigned int configured:1;
  54.  
  55.     unsigned int redrawPending:1;
  56.     } flags;
  57. } Scroller;
  58.  
  59.  
  60.  
  61. #define DEFAULT_HEIGHT        60
  62. #define DEFAULT_WIDTH        SCROLLER_WIDTH
  63. #define DEFAULT_ARROWS_POSITION    WSAMinEnd
  64.  
  65.  
  66.  
  67. static void destroyScroller(Scroller *sPtr);
  68. static void paintScroller(Scroller *sPtr);
  69.  
  70. static void willResizeScroller();
  71. static void handleEvents(XEvent *event, void *data);
  72. static void handleActionEvents(XEvent *event, void *data);
  73.  
  74. static void handleMotion(Scroller *sPtr, int mouseX, int mouseY);
  75.  
  76.  
  77. W_ViewDelegate _ScrollerViewDelegate = {
  78.     NULL,
  79.     NULL,
  80.     NULL,
  81.     NULL,
  82.     willResizeScroller
  83. };
  84.  
  85.  
  86.  
  87.           
  88. WMScroller*
  89. WMCreateScroller(WMWidget *parent)
  90. {
  91.     Scroller *sPtr;
  92.     
  93.     sPtr = wmalloc(sizeof(Scroller));
  94.     memset(sPtr, 0, sizeof(Scroller));
  95.     
  96.     sPtr->widgetClass = WC_Scroller;
  97.  
  98.     sPtr->view = W_CreateView(W_VIEW(parent));
  99.     if (!sPtr->view) {
  100.     wfree(sPtr);
  101.     return NULL;
  102.     }
  103.     sPtr->view->self = sPtr;
  104.  
  105.     sPtr->view->delegate = &_ScrollerViewDelegate;
  106.     
  107.     sPtr->flags.documentFullyVisible = 1;
  108.     
  109.     WMCreateEventHandler(sPtr->view, ExposureMask|StructureNotifyMask
  110.              |ClientMessageMask, handleEvents, sPtr);
  111.  
  112.     W_ResizeView(sPtr->view, DEFAULT_WIDTH, DEFAULT_WIDTH);
  113.     sPtr->flags.arrowsPosition = DEFAULT_ARROWS_POSITION;
  114.  
  115.     WMCreateEventHandler(sPtr->view, ButtonPressMask|ButtonReleaseMask
  116.              |EnterWindowMask|LeaveWindowMask|ButtonMotionMask,
  117.              handleActionEvents, sPtr);
  118.     
  119.     sPtr->flags.hitPart = WSNoPart;
  120.  
  121.     return sPtr;
  122. }
  123.  
  124.  
  125.  
  126. void
  127. WMSetScrollerArrowsPosition(WMScroller *sPtr, WMScrollArrowPosition position)
  128. {
  129.     sPtr->flags.arrowsPosition = position;
  130.     if (sPtr->view->flags.realized) {
  131.     paintScroller(sPtr);
  132.     }
  133. }
  134.  
  135.  
  136. static void
  137. willResizeScroller(W_ViewDelegate *self, WMView *view, 
  138.            unsigned int *width, unsigned int *height)
  139. {
  140.     WMScroller *sPtr = (WMScroller*)view->self;
  141.     
  142.     if (*width > *height) {
  143.     sPtr->flags.horizontal = 1;
  144.     *height = SCROLLER_WIDTH;
  145.     } else {
  146.     sPtr->flags.horizontal = 0;
  147.     *width = SCROLLER_WIDTH;
  148.     }
  149. }
  150.  
  151.  
  152. void
  153. WMSetScrollerAction(WMScroller *sPtr, WMAction *action, void *clientData)
  154. {
  155.     CHECK_CLASS(sPtr, WC_Scroller);
  156.     
  157.     sPtr->action = action;
  158.     
  159.     sPtr->clientData = clientData;
  160. }
  161.  
  162.  
  163. void
  164. WMSetScrollerParameters(WMScroller *sPtr, float floatValue,
  165.             float knobProportion)
  166. {
  167.     CHECK_CLASS(sPtr, WC_Scroller);
  168.  
  169.     if (floatValue < 0.0)
  170.     sPtr->floatValue = 0.0;
  171.     else if (floatValue > 1.0)
  172.     sPtr->floatValue = 1.0;
  173.     else
  174.     sPtr->floatValue = floatValue;
  175.  
  176.     if (knobProportion <= 0.0) {
  177.     
  178.     sPtr->knobProportion = 0.0;
  179.     sPtr->flags.documentFullyVisible = 0;
  180.     
  181.     } else if (knobProportion >= 1.0) {
  182.     
  183.     sPtr->knobProportion = 1.0;
  184.     sPtr->flags.documentFullyVisible = 1;
  185.     
  186.     } else {
  187.     sPtr->knobProportion = knobProportion;
  188.     sPtr->flags.documentFullyVisible = 0;
  189.     }
  190.  
  191.     if (sPtr->view->flags.realized)
  192.     paintScroller(sPtr);
  193. }
  194.  
  195.  
  196. float
  197. WMGetScrollerKnobProportion(WMScroller *sPtr)
  198. {
  199.     CHECK_CLASS(sPtr, WC_Scroller);
  200.     
  201.     return sPtr->knobProportion;
  202. }
  203.  
  204.  
  205. float
  206. WMGetScrollerValue(WMScroller *sPtr)
  207. {
  208.     CHECK_CLASS(sPtr, WC_Scroller);
  209.     
  210.     return sPtr->floatValue;
  211. }
  212.  
  213.  
  214. WMScrollerPart
  215. WMGetScrollerHitPart(WMScroller *sPtr)
  216. {
  217.     CHECK_CLASS(sPtr, WC_Scroller);
  218.     
  219.     return sPtr->flags.hitPart;
  220. }
  221.  
  222.  
  223. static void
  224. paintArrow(WMScroller *sPtr, Drawable d, int part)
  225. /*
  226.  * part- 0 paints the decrement arrow, 1 the increment arrow
  227.  */
  228. {
  229.     WMView *view = sPtr->view;
  230.     WMScreen *scr = view->screen;
  231.     int ofs, bsize;
  232.     W_Pixmap *arrow;
  233.  
  234. #ifndef DOUBLE_BUFFER
  235.     GC gc = scr->lightGC;
  236. #endif
  237.     
  238.     bsize = SCROLLER_WIDTH - 4;
  239.  
  240.  
  241.     if (part == 0) { /* decrement button */
  242.     if (sPtr->flags.horizontal) {
  243.         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
  244.         ofs = view->size.width - 2*(bsize+1) - 1;
  245.         } else {
  246.         ofs = 2;
  247.         }
  248.         if (sPtr->flags.decrDown)
  249.         arrow = scr->hiLeftArrow;
  250.         else
  251.         arrow = scr->leftArrow;
  252.         
  253.     } else {
  254.         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
  255.         ofs = view->size.height - 2*(bsize+1) - 1;
  256.         } else {
  257.         ofs = 2;
  258.         }
  259.         if (sPtr->flags.decrDown)
  260.         arrow = scr->hiUpArrow;
  261.         else
  262.         arrow = scr->upArrow;
  263.     }
  264.         
  265. #ifndef DOUBLE_BUFFER
  266.     if (sPtr->flags.decrDown)
  267.         gc = WMColorGC(scr->white);
  268. #endif
  269.     } else { /* increment button */
  270.     if (sPtr->flags.horizontal) {
  271.         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
  272.         ofs = view->size.width - bsize+1 - 3;
  273.         } else {
  274.         ofs = 2 + bsize+1;
  275.         }
  276.         if (sPtr->flags.incrDown)
  277.         arrow = scr->hiRightArrow;
  278.         else
  279.         arrow = scr->rightArrow;
  280.     } else {
  281.         if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
  282.         ofs = view->size.height - bsize+1 - 3;
  283.         } else {
  284.         ofs = 2 + bsize+1;
  285.         }
  286.         if (sPtr->flags.incrDown)
  287.         arrow = scr->hiDownArrow;
  288.         else
  289.         arrow = scr->downArrow;
  290.     }
  291.     
  292. #ifndef DOUBLE_BUFFER
  293.     if (sPtr->flags.incrDown)
  294.         gc = scr->whiteGC;
  295. #endif
  296.     }
  297.     
  298.     
  299.     if (sPtr->flags.horizontal) {
  300.         
  301.     /* paint button */
  302. #ifndef DOUBLE_BUFFER
  303.     XFillRectangle(scr->display, d, gc,
  304.                ofs+1, 2+1, bsize+1-3, bsize-3);
  305. #else
  306.     if ((!part&&sPtr->flags.decrDown) || (part&&sPtr->flags.incrDown))
  307.         XFillRectangle(scr->display, d, WMColorGC(scr->white),
  308.                ofs+1, 2+1, bsize+1-3, bsize-3);
  309. #endif /* DOUBLE_BUFFER */
  310.     W_DrawRelief(scr, d, ofs, 2, bsize, bsize, WRRaised);
  311.     
  312.     /* paint arrow */
  313.     XSetClipMask(scr->display, scr->clipGC, arrow->mask);
  314.     XSetClipOrigin(scr->display, scr->clipGC,
  315.                ofs + (bsize - arrow->width) / 2, 
  316.                2 + (bsize - arrow->height) / 2);
  317.     
  318.     XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
  319.           0, 0, arrow->width, arrow->height,
  320.           ofs + (bsize - arrow->width) / 2, 
  321.           2 + (bsize - arrow->height) / 2);
  322.     
  323.     } else { /* vertical */
  324.     
  325.     /* paint button */
  326. #ifndef DOUBLE_BUFFER
  327.     XFillRectangle(scr->display, d, gc,
  328.                2+1, ofs+1, bsize-3, bsize+1-3);
  329. #else
  330.     if ((!part&&sPtr->flags.decrDown) || (part&&sPtr->flags.incrDown))
  331.         XFillRectangle(scr->display, d, WMColorGC(scr->white),
  332.                2+1, ofs+1, bsize-3, bsize+1-3);
  333. #endif /* DOUBLE_BUFFER */
  334.     W_DrawRelief(scr, d, 2, ofs, bsize, bsize, WRRaised);
  335.     
  336.     /* paint arrow */
  337.     
  338.     XSetClipMask(scr->display, scr->clipGC, arrow->mask);
  339.     XSetClipOrigin(scr->display, scr->clipGC, 
  340.                2 + (bsize - arrow->width) / 2, 
  341.                ofs + (bsize - arrow->height) / 2);
  342.     XCopyArea(scr->display, arrow->pixmap, d, scr->clipGC,
  343.           0, 0, arrow->width, arrow->height,
  344.           2 + (bsize - arrow->width) / 2, 
  345.           ofs + (bsize - arrow->height) / 2);
  346.     }
  347. }    
  348.  
  349.  
  350. static int
  351. knobLength(Scroller *sPtr)
  352. {
  353.     int tmp, length;
  354.  
  355.     
  356.     if (sPtr->flags.horizontal)
  357.     length = sPtr->view->size.width - 4;
  358.     else
  359.     length = sPtr->view->size.height - 4;
  360.  
  361.     if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
  362.     length -= (SCROLLER_WIDTH - 4 + 1)*2;
  363.     } else if (sPtr->flags.arrowsPosition == WSAMinEnd) {
  364.     length -= (SCROLLER_WIDTH - 4 + 1)*2;
  365.     }
  366.     
  367.     tmp = (int)((float)length * sPtr->knobProportion + 0.5);
  368.     /* keep minimum size */
  369.     if (tmp < SCROLLER_WIDTH-4)
  370.     tmp = SCROLLER_WIDTH-4;
  371.     
  372.     return tmp;
  373. }
  374.  
  375.  
  376. static void
  377. paintScroller(Scroller *sPtr)
  378. {
  379.     WMView *view = sPtr->view;
  380.     WMScreen *scr = view->screen;
  381. #ifdef DOUBLE_BUFFER
  382.     Pixmap d;
  383. #else
  384.     Drawable d = view->window;
  385. #endif
  386.     int length, ofs;
  387.     float knobP, knobL;
  388.     
  389.     
  390. #ifdef DOUBLE_BUFFER
  391.     d = XCreatePixmap(scr->display, view->window, view->size.width, 
  392.               view->size.height, scr->depth);
  393.     XFillRectangle(scr->display, d, WMColorGC(scr->gray), 0, 0, 
  394.            view->size.width, view->size.height);
  395. #endif
  396.     
  397.     XDrawRectangle(scr->display, d, WMColorGC(scr->black), 0, 0,
  398.            view->size.width-1, view->size.height-1);
  399. #ifndef DOUBLE_BUFFER
  400.     XDrawRectangle(scr->display, d, WMColorGC(scr->gray), 1, 1,
  401.            view->size.width-3, view->size.height-3);
  402. #endif
  403.  
  404.     if (sPtr->flags.horizontal)
  405.     length = view->size.width - 4;
  406.     else
  407.     length = view->size.height - 4;
  408.     
  409.     if (sPtr->flags.documentFullyVisible) {
  410.     XFillRectangle(scr->display, d, scr->stippleGC, 2, 2,
  411.                view->size.width-4, view->size.height-4);
  412.     } else {
  413.     if (sPtr->flags.arrowsPosition==WSAMaxEnd) {
  414.         ofs = 0;
  415.         length -= (SCROLLER_WIDTH - 4 + 1)*2;
  416.     } else if (sPtr->flags.arrowsPosition==WSAMinEnd) {    
  417.         ofs = (SCROLLER_WIDTH - 4 + 1)*2;
  418.         length -= (SCROLLER_WIDTH - 4 + 1)*2;
  419.     } else {
  420.         ofs = 0;
  421.     }
  422.     
  423.     knobL = (float)knobLength(sPtr);
  424.  
  425.     knobP = sPtr->floatValue * ((float)length - knobL);
  426.  
  427.     
  428.     if (sPtr->flags.horizontal) {
  429.         /* before */
  430.         XFillRectangle(scr->display, d, scr->stippleGC,
  431.                ofs+2, 2, (int)knobP, view->size.height-4);
  432.         
  433.         /* knob */
  434. #ifndef DOUBLE_BUFFER
  435.         XFillRectangle(scr->display, d, scr->lightGC,
  436.                ofs+2+(int)knobP+2, 2+2, (int)knobL-4,
  437.                view->size.height-4-4);
  438. #endif
  439.         W_DrawRelief(scr, d, ofs+2+(int)knobP, 2, (int)knobL,
  440.              view->size.height-4, WRRaised);
  441.         
  442.         XCopyArea(scr->display, scr->scrollerDimple->pixmap, d, 
  443.               scr->copyGC, 0, 0,
  444.               scr->scrollerDimple->width, scr->scrollerDimple->height,
  445.               ofs+2+(int)knobP+((int)knobL-scr->scrollerDimple->width-1)/2,
  446.               (view->size.height-scr->scrollerDimple->height-1)/2);
  447.         
  448.         /* after */
  449.         if ((int)(knobP+knobL) < length)
  450.         XFillRectangle(scr->display, d, scr->stippleGC,
  451.                    ofs+2+(int)(knobP+knobL), 2,
  452.                    length-(int)(knobP+knobL),
  453.                    view->size.height-4);
  454.     } else {
  455.         /* before */
  456.         if (knobP>0.0)
  457.         XFillRectangle(scr->display, d, scr->stippleGC,
  458.                    2, ofs+2, view->size.width-4, (int)knobP);
  459.         
  460.         /* knob */
  461. #ifndef DOUBLE_BUFFER
  462.         XFillRectangle(scr->display, d, scr->lightGC,
  463.                2+2, ofs+2+(int)knobP+2,
  464.                view->size.width-4-4, (int)knobL-4);
  465. #endif
  466.         XCopyArea(scr->display, scr->scrollerDimple->pixmap, d, 
  467.               scr->copyGC, 0, 0,
  468.               scr->scrollerDimple->width, scr->scrollerDimple->height,
  469.               (view->size.width-scr->scrollerDimple->width-1)/2,
  470.               ofs+2+(int)knobP+((int)knobL-scr->scrollerDimple->height-1)/2);
  471.  
  472.         W_DrawRelief(scr, d, 2, ofs+2+(int)knobP,
  473.              view->size.width-4, (int)knobL, WRRaised);
  474.  
  475.         /* after */
  476.         if ((int)(knobP+knobL) < length)
  477.         XFillRectangle(scr->display, d, scr->stippleGC,
  478.                    2, ofs+2+(int)(knobP+knobL),
  479.                    view->size.width-4, 
  480.                    length-(int)(knobP+knobL));
  481.     }
  482.  
  483.     if (sPtr->flags.arrowsPosition != WSANone) {
  484.         paintArrow(sPtr, d, 0);
  485.         paintArrow(sPtr, d, 1);
  486.     }
  487.     }
  488.  
  489. #ifdef DOUBLE_BUFFER
  490.     XCopyArea(scr->display, d, view->window, scr->copyGC, 0, 0, 
  491.           view->size.width, view->size.height, 0, 0);
  492.     XFreePixmap(scr->display, d);
  493. #endif
  494. }
  495.  
  496.  
  497.  
  498. static void
  499. handleEvents(XEvent *event, void *data)
  500. {
  501.     Scroller *sPtr = (Scroller*)data;
  502.  
  503.     CHECK_CLASS(data, WC_Scroller);
  504.  
  505.  
  506.     switch (event->type) {
  507.      case Expose:
  508.     if (event->xexpose.count==0)
  509.         paintScroller(sPtr);
  510.     break;
  511.     
  512.      case DestroyNotify:
  513.     destroyScroller(sPtr);
  514.     break;
  515.     }
  516. }
  517.  
  518.  
  519.  
  520. /*
  521.  * locatePointInScroller-
  522.  *     Return the part of the scroller where the point is located.
  523.  */
  524. static WMScrollerPart
  525. locatePointInScroller(Scroller *sPtr, int x, int y, int alternate)
  526. {
  527.     int width = sPtr->view->size.width;
  528.     int height = sPtr->view->size.height;
  529.     int c, p1, p2, p3, p4, p5, p6;
  530.     int knobL, slotL;
  531.  
  532.  
  533.     /* if there is no knob... */
  534.     if (sPtr->flags.documentFullyVisible)
  535.     return WSKnobSlot;
  536.     
  537.     if (sPtr->flags.horizontal)
  538.     c = x;
  539.     else
  540.     c = y;
  541.  
  542.     /*     p1  p2           p3           p4   p5   p6
  543.      * |   |   |###########|             |#####|   |   |   
  544.      * | < | > |###########|      O      |#####| < | > |
  545.      * |   |   |###########|             |#####|   |   |
  546.      */
  547.     
  548.     if (sPtr->flags.arrowsPosition == WSAMinEnd) {
  549.     p1 = 18;
  550.     p2 = 36;
  551.  
  552.     if (sPtr->flags.horizontal) {
  553.         slotL = width - 36;
  554.         p5 = width;
  555.     } else {
  556.         slotL = height - 36;
  557.         p5 = height;
  558.     }
  559.     p6 = p5;
  560.     } else if (sPtr->flags.arrowsPosition == WSAMaxEnd) {
  561.     if (sPtr->flags.horizontal) {
  562.         slotL = width - 36;
  563.         p6 = width - 18;
  564.     } else {
  565.         slotL = height - 36;
  566.         p6 = height - 18;
  567.     }
  568.     p5 = p6 - 18;
  569.     
  570.     p1 = p2 = 0;
  571.     } else {
  572.     /* no arrows */
  573.     p1 = p2 = 0;
  574.  
  575.     if (sPtr->flags.horizontal) {
  576.         slotL = p5 = p6 = width;
  577.     } else {
  578.         slotL = p5 = p6 = height;
  579.     }
  580.     }
  581.     
  582.     knobL = knobLength(sPtr);
  583.     p3 = p2 + (int)((float)(slotL-knobL) * sPtr->floatValue);
  584.     p4 = p3 + knobL;
  585.  
  586.     /* uses a mix of the NS and Win ways of doing scroll page */
  587.     if (c <= p1)
  588.     return alternate ? WSDecrementPage : WSDecrementLine;
  589.     else if (c <= p2)
  590.     return alternate ? WSIncrementPage : WSIncrementLine;
  591.     else if (c <= p3)
  592.     return WSDecrementPage;
  593.     else if (c <= p4)
  594.     return WSKnob;
  595.     else if (c <= p5)
  596.     return WSIncrementPage;
  597.     else if (c <= p6)
  598.     return alternate ? WSDecrementPage : WSDecrementLine;
  599.     else
  600.     return alternate ? WSIncrementPage : WSIncrementLine;
  601. }
  602.  
  603.  
  604.  
  605. static void
  606. handlePush(Scroller *sPtr, int pushX, int pushY, int alternate)
  607. {
  608.     WMScrollerPart part;
  609.     int doAction = 0;
  610.     
  611.     part = locatePointInScroller(sPtr, pushX, pushY, alternate);
  612.     
  613.     sPtr->flags.hitPart = part;
  614.     
  615.     switch (part) {
  616.      case WSIncrementLine:
  617.     sPtr->flags.incrDown = 1;
  618.     doAction = 1;
  619.     break;
  620.  
  621.      case WSIncrementPage:
  622.     doAction = 1;
  623.     break;
  624.     
  625.      case WSDecrementLine:
  626.     sPtr->flags.decrDown = 1;
  627.     doAction = 1;
  628.     break;
  629.     
  630.      case WSDecrementPage:
  631.     doAction = 1;
  632.     break;
  633.     
  634.      case WSKnob:
  635.     sPtr->flags.draggingKnob = 1;
  636. #ifndef STRICT_NEXT_BEHAVIOUR
  637.     if (sPtr->flags.horizontal)
  638.         sPtr->dragPoint = pushX;
  639.     else
  640.         sPtr->dragPoint = pushY;
  641.  
  642.     {
  643.         int noButtons = (sPtr->flags.arrowsPosition == WSANone);
  644.         int length, knobP;
  645.  
  646.         if (sPtr->flags.horizontal)
  647.         length = sPtr->view->size.width - 4;
  648.         else
  649.         length = sPtr->view->size.height - 4;
  650.  
  651.         if (!noButtons)
  652.         length -= 36;
  653.  
  654.         knobP = (int)(sPtr->floatValue * (float)(length-knobLength(sPtr)));
  655.  
  656.         if (sPtr->flags.arrowsPosition == WSAMinEnd)
  657.         sPtr->dragPoint -= 2 + (noButtons ? 0 : 36) + knobP;
  658.         else 
  659.         sPtr->dragPoint -= 2 + knobP;
  660.     }
  661. #endif /* STRICT_NEXT_BEHAVIOUR */
  662.         /* This does not seem necesary here since we don't know yet if the
  663.          * knob will be dragged later. -Dan
  664.         handleMotion(sPtr, pushX, pushY); */
  665.     break;
  666.  
  667.      case WSKnobSlot:
  668.      case WSNoPart:
  669.     /* dummy */
  670.     break;
  671.     }
  672.     
  673.     if (doAction && sPtr->action) {
  674.     (*sPtr->action)(sPtr, sPtr->clientData);
  675.     }
  676. }
  677.  
  678.  
  679. static float
  680. floatValueForPoint(int slotOfs, int slotLength, int knobLength, int point)
  681. {
  682.     float floatValue = 0;
  683.     float position;
  684.  
  685. #ifdef STRICT_NEXT_BEHAVIOUR
  686.     if (point < slotOfs + knobLength/2)
  687.     position = (float)(slotOfs + knobLength/2);
  688.     else if (point > slotOfs + slotLength - knobLength/2)
  689.     position = (float)(slotOfs + slotLength - knobLength/2);
  690.     else
  691.     position = (float)point;
  692.     
  693.     floatValue = (position-(float)(slotOfs+slotLength/2))
  694.     /(float)(slotLength-knobLength);
  695. #else
  696.     /* Adjust the last point to lie inside the knob slot */    
  697.     if (point < slotOfs)
  698.     position = (float)slotOfs;
  699.     else if (point > slotOfs + slotLength)
  700.     position = (float)(slotOfs + slotLength);
  701.     else
  702.     position = (float)point;
  703.  
  704.     /* Compute the float value */
  705.     floatValue = (position-(float)slotOfs) / (float)(slotLength-knobLength);
  706. #endif
  707.     
  708.     return floatValue;
  709. }
  710.  
  711.  
  712. static void
  713. handleMotion(Scroller *sPtr, int mouseX, int mouseY)
  714. {
  715.     int slotOffset;
  716.     int slotLength;
  717.     int noButtons = (sPtr->flags.arrowsPosition == WSANone);
  718.     
  719.     if (sPtr->flags.arrowsPosition == WSAMinEnd)
  720.     slotOffset = 2 + (noButtons ? 0 : 36);
  721.     else 
  722.     slotOffset = 2;
  723.  
  724.     if (sPtr->flags.draggingKnob) {
  725.     float newFloatValue;    
  726. #ifdef STRICT_NEXT_BEHAVIOUR
  727.     if (sPtr->flags.horizontal) {
  728.         slotLength = sPtr->view->size.width-4-(noButtons ? 0 : 36);
  729.         newFloatValue = floatValueForPoint(slotOffset, slotLength,
  730.                        (int)(slotLength*sPtr->knobProportion),
  731.                        mouseX);
  732.     } else {
  733.         slotLength = sPtr->view->size.height-4-(noButtons ? 0 : 36);
  734.         newFloatValue = floatValueForPoint(slotOffset, slotLength,
  735.                        (int)(slotLength*sPtr->knobProportion),
  736.                        mouseY);
  737.     }
  738. #else
  739.     if (sPtr->flags.horizontal) {
  740.         slotLength = sPtr->view->size.width-4-(noButtons ? 0 : 36);
  741.         newFloatValue = floatValueForPoint(slotOffset, slotLength,
  742.                        (int)(slotLength*sPtr->knobProportion),
  743.                        mouseX-sPtr->dragPoint);
  744.     } else {
  745.         slotLength = sPtr->view->size.height-4-(noButtons ? 0 : 36);
  746.         newFloatValue = floatValueForPoint(slotOffset, slotLength,
  747.                        (int)(slotLength*sPtr->knobProportion),
  748.                        mouseY-sPtr->dragPoint);
  749.     }
  750. #endif /* !STRICT_NEXT_BEHAVIOUR */
  751.     WMSetScrollerParameters(sPtr, newFloatValue, sPtr->knobProportion);
  752.     if (sPtr->action) {
  753.         (*sPtr->action)(sPtr, sPtr->clientData);
  754.     }
  755.     } else {
  756.     int part;
  757.     
  758.     part = locatePointInScroller(sPtr, mouseX, mouseY, False);
  759.  
  760.     sPtr->flags.hitPart = part;
  761.  
  762.     if (part == WSIncrementLine && sPtr->flags.decrDown) {
  763.         sPtr->flags.decrDown = 0;
  764.         sPtr->flags.incrDown = 1;
  765.     } else if (part == WSDecrementLine && sPtr->flags.incrDown) {
  766.         sPtr->flags.incrDown = 0;
  767.         sPtr->flags.decrDown = 1;
  768.     } else if (part != WSIncrementLine && part != WSDecrementLine) {
  769.         sPtr->flags.incrDown = 0;
  770.         sPtr->flags.decrDown = 0;
  771.     }
  772.     } 
  773. }
  774.  
  775.  
  776. static void
  777. autoScroll(void *clientData)
  778. {
  779.     Scroller *sPtr = (Scroller*)clientData;
  780.     
  781.     if (sPtr->action) {
  782.     (*sPtr->action)(sPtr, sPtr->clientData);
  783.     }
  784.     sPtr->timerID= WMAddTimerHandler(AUTOSCROLL_DELAY, autoScroll, clientData);
  785. }
  786.  
  787.  
  788. static void
  789. handleActionEvents(XEvent *event, void *data)
  790. {
  791.     Scroller *sPtr = (Scroller*)data;
  792.     int id, dd;
  793.  
  794.     
  795.     /* check if we're really dealing with a scroller, as something
  796.      * might have gone wrong in the event dispatching stuff */
  797.     CHECK_CLASS(sPtr, WC_Scroller);
  798.     
  799.     id = sPtr->flags.incrDown;
  800.     dd = sPtr->flags.decrDown;
  801.     
  802.     switch (event->type) {
  803.      case EnterNotify:
  804.     
  805.     break;
  806.     
  807.      case LeaveNotify:
  808.     if (sPtr->timerID) {
  809.         WMDeleteTimerHandler(sPtr->timerID);
  810.         sPtr->timerID = NULL;
  811.     }
  812.     sPtr->flags.incrDown = 0;
  813.     sPtr->flags.decrDown = 0;
  814.     break;
  815.     
  816.      case ButtonPress:
  817.     /* FIXME: change Mod1Mask with something else */
  818.     handlePush(sPtr, event->xbutton.x, event->xbutton.y,
  819.            (event->xbutton.state & Mod1Mask)
  820.            ||event->xbutton.button==Button2);
  821.     /* continue scrolling if pushed on the buttons */
  822.     if (sPtr->flags.hitPart == WSIncrementLine
  823.         || sPtr->flags.hitPart == WSDecrementLine) {
  824.         sPtr->timerID = WMAddTimerHandler(AUTOSCROLL_INITIAL_DELAY,
  825.                           autoScroll, sPtr);
  826.     }
  827.     break;
  828.  
  829.      case ButtonRelease:
  830.     if (sPtr->flags.draggingKnob) {
  831.         if (sPtr->action) {
  832.         (*sPtr->action)(sPtr, sPtr->clientData);
  833.         }
  834.     }
  835.     if (sPtr->timerID) {
  836.         WMDeleteTimerHandler(sPtr->timerID);
  837.         sPtr->timerID = NULL;
  838.     }
  839.     sPtr->flags.incrDown = 0;
  840.     sPtr->flags.decrDown = 0;
  841.     sPtr->flags.draggingKnob = 0;
  842.     break;
  843.  
  844.      case MotionNotify:
  845.     handleMotion(sPtr, event->xbutton.x, event->xbutton.y);
  846.     if (sPtr->timerID && sPtr->flags.hitPart != WSIncrementLine
  847.         && sPtr->flags.hitPart != WSDecrementLine) {
  848.         WMDeleteTimerHandler(sPtr->timerID);
  849.         sPtr->timerID = NULL;
  850.     }
  851.     break;
  852.     }
  853.     if (id != sPtr->flags.incrDown || dd != sPtr->flags.decrDown)
  854.     paintScroller(sPtr);
  855. }
  856.  
  857.  
  858.  
  859. static void
  860. destroyScroller(Scroller *sPtr)
  861. {
  862.     /* we don't want autoscroll try to scroll a freed widget */
  863.     if (sPtr->timerID) {
  864.     WMDeleteTimerHandler(sPtr->timerID);
  865.     }
  866.  
  867.     wfree(sPtr);
  868. }
  869.  
  870.